﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using PostSharp.Aspects;
using PostSharp.Aspects.Advices;
using PostSharp.Extensibility;
using PostSharp.Reflection;

namespace QuickRunnable
{
    public static class Program2 {
        public static string current_string;
        public static string previous_string;

        private static void Main(string[] args) {
            const int ITEMS = 1000000;
            IMyString[] no_attrib = new IMyString[ITEMS];
            IMyString[] attrib = new IMyString[ITEMS];
            for (int i = 0; i < ITEMS; i++) no_attrib[i] = new PropertyTesterNoAttribute(i.ToString());
            for (int i = 0; i < ITEMS; i++) attrib[i] = new PropertyTester(i.ToString());

            Console.WriteLine("NoAspect"); 
            GoGetter(no_attrib);
            Console.WriteLine(" - " + current_string);

            current_string = "-";
            Console.WriteLine("Aspect"); 
            GoGetter(attrib);
            Console.WriteLine(" - " + current_string);

            Console.WriteLine();
            Console.WriteLine("---Setters");
            Console.WriteLine();

            current_string = "-";
            Console.WriteLine("NoAspect");
            GoSetter(no_attrib);
            Console.WriteLine(" - " +current_string);
            Console.WriteLine("Aspect");

            current_string = "-";
            GoSetter(attrib);
            Console.WriteLine(" - " + current_string);
            Console.ReadLine();
        }

        private static void GoGetter(IMyString[] items ) {
            var stop = Stopwatch.StartNew();
            for( int i=0 ; i < items.Length ; i++ ) {
                previous_string = current_string;
                current_string = items[ i ].MyString;
            }
            stop.Stop();
            Console.WriteLine( stop.ElapsedMilliseconds );
        }


        private static void GoSetter(IMyString[] items) {
            var stop = Stopwatch.StartNew();
            for (int i = 0; i < items.Length; i++)
            {
                items[i].MyString = current_string;
                current_string = items[i].MyString;
            }
            stop.Stop();
            Console.WriteLine(stop.ElapsedMilliseconds);
        }
    }

    public interface IMyString {
        String MyString { get; set; }
    }

    public class PropertyTesterNoAttribute : IMyString
    {

        public PropertyTesterNoAttribute(String str) { MyString = str; }
        private String m_MyString;
        public String m_OtherState;

        public String MyString { get; set; }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };
    }

    [GetterAspect(AttributeTargetMembers = "get_*")]
    [SetterAspect(AttributeTargetMembers = "set_*")]
    public class PropertyTester : IMyString {

        public PropertyTester(String str) { MyString = str; }
        public String MyString { get; set; }
    }
    [Serializable]
    public class GetterAspect : OnMethodBoundaryAspect
    {
        public static int TotalCalls = 2;

        public sealed override void OnEntry(MethodExecutionArgs args) { TotalCalls--; }
        public sealed override void OnSuccess(MethodExecutionArgs args) { }
        public sealed override void OnExit(MethodExecutionArgs args) { TotalCalls++; }
    }


    [Serializable]
    public class SetterAspect : OnMethodBoundaryAspect {
        public static int TotalCalls = 2;
        public sealed override void OnEntry( MethodExecutionArgs args ) {
            if (args.Arguments[0].Equals("1")) return;
            TotalCalls++;
        }
        public sealed override void OnSuccess(MethodExecutionArgs args){}
        public sealed override void OnExit(MethodExecutionArgs args) {
            if( Equals( args.ReturnValue, "1" ) ) return;
            TotalCalls--;
        }
    }

    [Serializable]
    [MulticastAttributeUsage(MulticastTargets.Class,
                              Inheritance = MulticastInheritance.Strict)]
    public sealed class PropertyAspect : InstanceLevelAspect
    {

        /// <summary>
        /// Method intercepting any call to a property setter.
        /// </summary>
        /// <param name="args">Aspect arguments.</param>
        [OnLocationGetValueAdvice,
         MulticastPointcut(Targets = MulticastTargets.Property,
             Attributes = MulticastAttributes.Instance)]
        public void OnPropertyGet(LocationInterceptionArgs args)
        {
            //args.Value = "A";
            args.ProceedGetValue();
        }

        [ImportMember("OnPropertyChanged", IsRequired = false)]
        public Action<string> OnPropertyChangedMethod = delegate(string s) { };

        /// <summary>
        /// Method intercepting any call to a property setter.
        /// </summary>
        /// <param name="args">Aspect arguments.</param>
        [OnLocationSetValueAdvice,
         MulticastPointcut(Targets = MulticastTargets.Property,
             Attributes = MulticastAttributes.Instance)]
        public void OnPropertySet(LocationInterceptionArgs args)
        {
            if (args.Value.Equals(args.GetCurrentValue())) return;
            args.Value = "SETA";
            args.ProceedSetValue();

            this.OnPropertyChangedMethod.Invoke((String)args.Value);
        }
    }

    [Serializable]
    public sealed class TraceAttribute : OnMethodBoundaryAspect
    {
        private readonly string category;

        public TraceAttribute(string category)
        {
            this.category = category;
        }

        public string Category { get { return category; } }
        public override void OnEntry(MethodExecutionArgs args)
        {
            Console.WriteLine("Entery: " + args.Method );
        }
        public override void OnExit(MethodExecutionArgs args)
        {
            Console.WriteLine("Exit: " + args.Method);
        }
    }
}
